package com.linoer.cloud.alo.common.aspect.proxy.jdk;

import com.linoer.cloud.alo.common.aspect.proxy.AfterAdvice;
import com.linoer.cloud.alo.common.aspect.proxy.BeforeAdvice;
import com.linoer.cloud.alo.common.aspect.proxy.ExceptionAdvice;
import com.linoer.cloud.alo.common.aspect.proxy.IAdvice;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;

/**
 * @Classname ProxyFactory
 * @Description jdk代理工厂
 * @Date 2021/7/27 3:53 下午
 * @Created by muhenglv
 */
public class ProxyFactory {
    // 待代理对象
    private Object target;
    private List<BeforeAdvice> beforeAdvices = new ArrayList<>(8);
    private List<AfterAdvice> afterAdvices = new ArrayList<>(8);
    private List<ExceptionAdvice> exceptionAdvices = new ArrayList<>(8);

    /**
     * 构造方法, 初始化待代理对象和增强点
     * @param target    待代理对象
     * @param advices   增强点, 顺序处理
     */
    private ProxyFactory(Object target, IAdvice... advices) {
        this.target = target;
        if (advices != null && advices.length > 0) {
            for (IAdvice advice : advices) {
                if (advice instanceof AfterAdvice) {
                    this.afterAdvices.add((AfterAdvice) advice);
                } else if (advice instanceof BeforeAdvice) {
                    this.beforeAdvices.add((BeforeAdvice) advice);
                } else if (advice instanceof ExceptionAdvice) {
                    this.exceptionAdvices.add((ExceptionAdvice) advice);
                }
            }
        }
    }

    public static Object getProxyInstance(Object target, IAdvice... advices) {
        return new ProxyFactory(target, advices).getInstance();
    }

    /**
     * 获取代理对象
     * @return  代理对象
     */
    private Object getInstance() {
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), this.target.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Object result = null;
                beforeAdvices.forEach(IAdvice::process);
                try {
                    result = method.invoke(target, args);
                } catch (Exception e) {
                    exceptionAdvices.forEach(IAdvice::process);
                } finally {
                    afterAdvices.forEach(IAdvice::process);
                }
                return result;
            }
        });
    }

}
